/*
 * Copyright (c) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "drawing_bench.h"
#include "display_manager.h"
#include "drawing_bench_demo_draw_path.h"
#include "drawing_bench_demo_draw_rect.h"
#include "drawing_bench_demo_draw_line.h"
#include "drawing_bench_test_case_beizer.h"
using namespace OHOS;
using namespace OHOS::Rosen;
using namespace std;

namespace {
std::unordered_map<std::string, std::function<std::shared_ptr<TestBase>()>> FunctionalCpuMap = {
    {"drawrect", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawRectTest>();}},
    {"drawpath", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawPathTest>();}},
    {"drawline", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawLineTest>();}},
    // {"drawbench", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawBenchTest>();}},
};

std::unordered_map<std::string, std::function<std::shared_ptr<TestBase>()>> PerformanceCpuMap = {
    {"drawrect", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawRectTest>();}},
    {"drawpath", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawPathTest>();}},
    {"drawline", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawLineTest>();}},
    // {"drawbench", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawBenchTest>();}},
};
std::unordered_map<std::string, std::function<std::shared_ptr<TestBase>()>> FunctionalGpuUpScreenMap = {
    {"drawrect", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawRectTest>();}},
    {"drawpath", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawPathTest>();}},
    {"drawline", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawLineTest>();}},
    // {"drawbench", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawBenchTest>();}},
};

std::unordered_map<std::string, std::function<std::shared_ptr<TestBase>()>> PerformanceGpuUpScreenMap = {
    {"drawrect", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawRectTest>();}},
    {"drawpath", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawPathTest>();}},
    {"drawline", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawLineTest>();}},
    {"drawbenchbeizer", []() -> std::shared_ptr<TestBase> {return std::make_shared<DrawBenchBeizer>();}},    
};
} // namespace

namespace OHOS {
namespace Rosen {
DrawingBench::DrawingBench(int argc, char* argv[])
{
    argc_ = argc;
    for (int i = 0; i < argc_; i++) {
        std::string str = argv[i];
        argv_.emplace_back(str);
    }
};

DrawingBench::~DrawingBench() {
    if (window_) {
        window_->Hide();
        window_->Destroy();
    }
};

int DrawingBench::Test()
{
    cout << "eg: drawing_bench function {cpu | gpu} {caseName?} {displayTime?}" << endl;
    cout << "eg: drawing_bench performance {cpu | gpu} caseName count {displayTime?}" << endl;
    if (argc_ < 3) {
        return RET_PARAM_ERR;
    }

    testType_ = argv_[1];
    drawingType_ = argv_[2];
    
    if (testType_ == "function") {
        if (drawingType_ == "cpu") {
            return TestFunctionalCpu();
        } else if (drawingType_ == "gpu") {
            return TestFunctionGpuUpScreen();
        }
    } else if (testType_ == "performance") {
        if (drawingType_ == "cpu") {
            return TestPerformanceCpu();
        } else if (drawingType_ == "gpu") {
            return TestPerformanceGpuUpScreen();
        }
    }
    
    return RET_PARAM_ERR;
}
  
int DrawingBench::InitWindow()
{
    cout << "Init Rosen Backend!" << endl;

    rootNode_ = RSRootNode::Create();
    if (rootNode_ == nullptr) {
        cout << "Failed to create rootNode" << endl;
        return RET_FAILED;
    }
    rootNode_->SetBounds(0, 0, rect_.width_, rect_.height_);
    rootNode_->SetFrame(0, 0, rect_.width_, rect_.height_);
    rootNode_->SetBackgroundColor(Drawing::Color::COLOR_WHITE);

    rsUiDirector_->SetRoot(rootNode_->GetId());
    canvasNode_ = RSCanvasNode::Create();
    if (canvasNode_ == nullptr) {
        cout << "Failed to create canvasNode" << endl;
        return RET_FAILED;
    }
    canvasNode_->SetFrame(0, 0, rect_.width_, rect_.height_);
    rootNode_->AddChild(canvasNode_, -1);
    return RET_OK;
}

int DrawingBench::createWindow()
{
    cout << "create window start" << endl;

    int32_t defaultWidth = 720;
    int32_t defaultHeight = 1280;
    sptr<Display> display = DisplayManager::GetInstance().GetDefaultDisplay();
    if (display != nullptr) {
        defaultWidth = display->GetWidth();
        defaultHeight = display->GetHeight();
    }
    cout << "display:" << defaultWidth << "*" << defaultHeight << endl;


    string demoName = "drawing_bench";
    RSSystemProperties::GetUniRenderEnabled();
    sptr<WindowOption> option = new WindowOption();
    option->SetWindowType(WindowType::APP_MAIN_WINDOW_BASE);
    option->SetWindowMode(WindowMode::WINDOW_MODE_FLOATING);
    option->SetWindowRect({ 0, 0, defaultWidth, defaultHeight });
    window_ = Window::Create(demoName, option);
    if (window_ == nullptr) {
        cout << "Failed to create window" << endl;
        return RET_FAILED;
    }

    window_->Show();
    usleep(SLEEP_TIME);
    auto rect = window_->GetRect();
    int count = 0;
    while (rect.width_ == 0 && rect.height_ == 0 && count < MAX_TRY_NUMBER) {
        cout << "drawing_bench create window failed: " << rect.width_ << " " << rect.height_ << endl;
        window_->Hide();
        window_->Destroy();
        window_ = Window::Create(demoName, option);
        window_->Show();
        usleep(SLEEP_TIME);
        rect = window_->GetRect();
        count++;
    }
    auto surfaceNode = window_->GetSurfaceNode();
    
    rsUiDirector_ = RSUIDirector::Create();
    if (rsUiDirector_ == nullptr) {
        cout << "Failed to create rsUiDirector_" << endl;
        return RET_FAILED;
    }
    rsUiDirector_->Init();
    RSTransaction::FlushImplicitTransaction();
    sleep(1);
    rsUiDirector_->SetRSSurfaceNode(surfaceNode);
    rect_ = rect;
    int ret = InitWindow();
    if (ret != RET_OK) {
        return ret;
    }
    rsUiDirector_->SendMessages();
    sleep(1);
    cout << "create window success: " << rect.width_ << " * " << rect.height_ << endl;
    return RET_OK;
}

int DrawingBench::GetFunctionalParam()
{
    // drawing_bench functional {cpu | gpu} {caseName?} {displayTime?}
    // Determine whether to execute all cases or a single case.
    if (argc_ <= 3) {
        caseName_ = ALL_TAST_CASE;
        return RET_OK;
    }

    caseName_ = argv_[3];
    if (FunctionalCpuMap.find(caseName_) == FunctionalCpuMap.end()) {
        cout << "TestCase not exist, please try again. All testcase:" << endl;
        for (auto map : FunctionalCpuMap) {
            cout << map.first << endl;
        }
        return RET_PARAM_ERR;
    }

    // The window automatically exits after being displayed for displayTime(s).
    if (argc_ > 4) {
        std::string displayTimeStr = argv_[4];
        displayTime_ = std::stoi(displayTimeStr);
    }
    return RET_OK;
}

int DrawingBench::TestFunctionalCpu()
{
    cout << "FunctionalTest start!" << endl;
    int ret = GetFunctionalParam();
    if (ret != RET_OK) {
        return ret;
    }

    ret = createWindow();
    if (ret != RET_OK) {
        return ret;
    }

    for (auto map : FunctionalCpuMap) {
        if ((caseName_ != ALL_TAST_CASE) && (caseName_ != map.first)) {
            continue;
        }

        auto canvas = canvasNode_->BeginRecording(rect_.width_, rect_.height_);
        auto testCase = map.second();
        if (testCase == nullptr) {
            cout << "Failed to create testcase" << endl;
            return RET_FAILED;
        }

        testCase->SetFileName(map.first);
        testCase->SetCanvas((TestDisplayCanvas*)(canvas));
        testCase->TestFunctionCpu();
        canvasNode_->FinishRecording();
        rsUiDirector_->SendMessages();
        usleep(16666); // 16.666ms 60fps
    }
    
    if (caseName_ != ALL_TAST_CASE) {
        cout << "wait " << displayTime_ << " s" << endl;
        sleep(displayTime_);
    }
    cout << "FunctionalTest end!" << endl;
    return RET_OK;
}

int DrawingBench::GetFunctionalGpuUpScreenParam()
{
    // drawing_bench functional gpu caseName displayTime
    if (argc_ <= 4) {
        return RET_PARAM_ERR;
    }

    caseName_ = argv_[3];
    auto map = FunctionalGpuUpScreenMap.find(caseName_);
    if (map == FunctionalGpuUpScreenMap.end()) {
        cout << "TestCase not exist, please try again. All testcase:" << endl;
        for (auto map : FunctionalGpuUpScreenMap) {
            cout << map.first << endl;
        }
        return RET_PARAM_ERR;
    }

    // The window automatically exits after being displayed for displayTime(s).
    if (argc_ > 4) {
        std::string displayTimeStr = argv_[4];
        displayTime_ = std::stoi(displayTimeStr);
    }
    return RET_OK;
}

int DrawingBench::TestFunctionGpuUpScreen()
{
    int ret = GetFunctionalGpuUpScreenParam();
    if (ret != RET_OK) {
        return ret;
    }

    cout << "TestFunctionGpuUpScreen start!" << endl;
    auto map = FunctionalGpuUpScreenMap.find(caseName_);
    auto testCase = map->second();
    if (testCase == nullptr) {
        cout << "Failed to create testcase" << endl;
        return RET_FAILED;
    }

    ret = createWindow();
    if (ret != RET_OK) {
        return ret;
    }

    auto canvas = canvasNode_->BeginRecording(rect_.width_, rect_.height_);
    testCase->SetCanvas((TestDisplayCanvas*)(canvas));
    testCase->TestFunctionGpuUpScreen();
    canvasNode_->FinishRecording();
    rsUiDirector_->SendMessages();
    
    cout << "wait " << displayTime_ << " s" << endl;
    sleep(displayTime_);
    cout << "TestFunctionGpuUpScreen end!" << endl;
    return RET_OK;
}

int DrawingBench::GetPerformanceParam()
{
    // drawing_bench performance {cpu | gpu} caseName count
    if (argc_ < 5) {
        return RET_PARAM_ERR;
    }

    caseName_ = argv_[3];

    // The number of repeated calls to the test object.
    std::string testCountStr = argv_[4];
    testCount_ = std::stoi(testCountStr);
    
    if (argc_ > 5) {
        std::string displayTimeStr = argv_[5];
        displayTime_ = std::stoi(displayTimeStr);
    }
    return RET_OK;
}

int DrawingBench::TestPerformanceCpu()
{
    int ret = GetPerformanceParam();
    if(ret != RET_OK) {
        return ret;
    }

    auto map = PerformanceCpuMap.find(caseName_);
    if (map == PerformanceCpuMap.end()) {
        cout << "TestCase not exist, please try again. All testcase:" << endl;
        for (auto map : PerformanceCpuMap) {
            cout << map.first << endl;
        }
        return RET_PARAM_ERR;
    }

    cout << "TestPerformanceCpu start!" << endl;
    ret = createWindow();
    if (ret != RET_OK) {
        return ret;
    }

    auto canvas = canvasNode_->BeginRecording(rect_.width_, rect_.height_);
    auto testCase = map->second();
    if (testCase == nullptr) {
        cout << "Failed to create testcase" << endl;
        return RET_FAILED;
    }
    testCase->SetCanvas((TestDisplayCanvas*)(canvas)); 
    testCase->SetTestCount(testCount_);    

    LOGI("DrawingApiTest Started: [0]");
    std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
    testCase->TestPerformanceCpu();
    std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
    int time = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
    LOGI("DrawingApiTest TotalApiCallTime: [%{public}d]", time);
    LOGI("DrawingApiTest TotalApiCallCount: [%{public}d]", testCount_);
    float avg = (float)time*1.0/testCount_;
    cout << "TestPerformanceCpu count:" << testCount_ << " time:" << time << "us" << " avg="<< avg<< "us"<<endl;
    canvasNode_->FinishRecording();
    rsUiDirector_->SendMessages();

    cout << "wait " << displayTime_ << " s" << endl;
    sleep(displayTime_);
    cout << "TestPerformanceCpu end!" << endl;
    return RET_OK;
}

int DrawingBench::TestPerformanceGpuUpScreen()
{
    int ret = GetPerformanceParam();
    if(ret != RET_OK) {
        return ret;
    }
 
    auto map = PerformanceGpuUpScreenMap.find(caseName_);
    if (map == PerformanceGpuUpScreenMap.end()) {
        cout << "TestCase not exist, please try again. All testcase:" << endl;
        for (auto map : PerformanceGpuUpScreenMap) {
            cout << map.first << endl;
        }
        return RET_PARAM_ERR;
    }

    cout << "TestPerformanceGpuUpScreen start!" << endl;
    ret = createWindow();
    if (ret != RET_OK) {
        return ret;
    }

    auto canvas = canvasNode_->BeginRecording(rect_.width_, rect_.height_);
    auto testCase = map->second();
    if (testCase == nullptr) {
        cout << "Failed to create testcase" << endl;
        return RET_FAILED;
    }
    testCase->SetCanvas((TestDisplayCanvas*)(canvas)); 
    testCase->SetTestCount(testCount_);    

    LOGI("DrawingApiTest Started: [0]");
    std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now();
    testCase->TestPerformanceGpuUpScreen();
    std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now();
    int time = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
    LOGI("DrawingApiTest TotalApiCallTime: [%{public}d]", time);
    LOGI("DrawingApiTest TotalApiCallCount: [%{public}d]", testCount_);
    float avg = (float)time*1.0/testCount_;
    cout << "TestPerformanceGpuUpScreen count:" << testCount_ << " time:" << time << "us" << " avg="<< avg<<"us"<<endl;
    canvasNode_->FinishRecording();
    rsUiDirector_->SendMessages();

    cout << "wait " << displayTime_ << " s" << endl;
    sleep(displayTime_);
    cout << "TestPerformanceGpuUpScreen end!" << endl;
    return RET_OK;
}
} // namespace Rosen
} // namespace OHOS

int main(int argc, char* argv[])
{
    std::shared_ptr<DrawingBench> drawingDemo= std::make_shared<DrawingBench>(argc, argv);
    if (drawingDemo == nullptr) {
        cout << "Failed to create DrawingBench" << endl;
        return 0;
    }
    int ret = drawingDemo->Test();
    if (ret == RET_PARAM_ERR) {
        cout << "Invalid parameter, please confirm" << endl;
    }
    return ret;
}
